#include "Table.h"


Table::Table(string t_name, string d_name):
	db_name(d_name), name(t_name), dropped(false)
{
	//column initialization
	string filename = db_name+"_"+name+".dat";

	bool bbuf;
	string sbuf;
	stringstream ssbuf;
	ifstream f(filename);

	vector<string> nlist;
	vector<string> tlist;
	vector<bool> elist;

	if(getline(f, sbuf, '\n'))
	{
		ssbuf << sbuf;
		while(! ssbuf.fail())
		{
			ssbuf >> sbuf;
			nlist.push_back(sbuf);
		}
		ssbuf.clear();
	}
	if(getline(f, sbuf, '\n'))
	{
		ssbuf << sbuf;
		while(! ssbuf.fail())
		{
			ssbuf >> sbuf;
			tlist.push_back(sbuf);
		}
		ssbuf.clear();
	}
	if(getline(f, sbuf, '\n'))
	{
		ssbuf << sbuf;
		while(! ssbuf.fail())
		{
			ssbuf >> bbuf;
			elist.push_back(bbuf);
		}
		ssbuf.clear();
	}

	if(tlist.size() == nlist.size() && nlist.size() == elist.size())
	{
		int ind = 0;
		while(getline(f, sbuf, '\n') && ind < static_cast<int>(tlist.size()))
		{
			Column* col_pointer;
			if(tlist[ind] == "i")
			{
				col_pointer = new Column_int(nlist[ind], elist[ind], stringstream(sbuf));
			}
			else if(tlist[ind] == "f")
			{
				col_pointer = new Column_float(nlist[ind], elist[ind], stringstream(sbuf));
			}
			else if(tlist[ind] == "s")
			{
				col_pointer = new Column_string(nlist[ind], elist[ind], stringstream(sbuf));
			}
			else if(tlist[ind] == "b")
			{
				col_pointer = new Column_bool(nlist[ind], elist[ind], stringstream(sbuf));
			}
			column.push_back(col_pointer);

			++ind;
			ssbuf.clear();
		}
	}

	f.close();
}


Table::~Table(void)
{
	if(dropped)
	{
		string filename = db_name+"_"+name+".dat";
		remove(filename.c_str());
	}
	else
	{
		save();
	}

	for(vector<Column*>::iterator it = column.begin(); it != column.end(); ++it)
	{
		if(*it != NULL)
		{
			delete *it;
		}
	}
}


void Table::col_add()
{
	string col_name = "";
	bool null_allowed = false;

	do
	{
		if(col_name != "")
		{
			cout << "[Error]: column already exists." << endl;
		}
		col_name = Validator::get<string>("Type the column's name: ");
	} while(col_exists(col_name));

	string ctemp = Validator::get<string>("Is null allowed? (y/n): ");
	null_allowed = ctemp == "y" || ctemp == "Y";

	string ct = "";
	do
	{
		if(ct != "")
		{
			cout << "[Error]: wrong column type. Choose from: bool/int/float/string" << endl;
		}
		ct = Validator::get<string>("Type the column's type (bool/int/float/string): ");
	} while(ct != "int" && ct != "float" && ct != "bool" && ct != "string");

	string sdat = "";

	Column* newcol;
	if(ct == "bool")
	{
		sdat = dummy_data(sdat, "0", col_dat_qt(), null_allowed);
		newcol = new Column_bool(col_name, null_allowed, stringstream(sdat));
		column.push_back(newcol);
		cout << "Column [" << col_name << "]" << " successfully created." << endl;
	}
	else if(ct == "int")
	{
		sdat = dummy_data(sdat, "0", col_dat_qt(), null_allowed);
		newcol = new Column_int(col_name, null_allowed, stringstream(sdat));
		column.push_back(newcol);
		cout << "Column [" << col_name << "]" << " successfully created." << endl;
	}
	else if(ct == "float")
	{
		sdat = dummy_data(sdat, "0.0", col_dat_qt(), null_allowed);
		newcol = new Column_float(col_name, null_allowed, stringstream(sdat));
		column.push_back(newcol);
		cout << "Column [" << col_name << "]" << " successfully created." << endl;
	}
	else if(ct == "string")
	{
		sdat = dummy_data(sdat, "-", col_dat_qt(), null_allowed);
		newcol = new Column_string(col_name, null_allowed, stringstream(sdat));
		column.push_back(newcol);
		cout << "Column [" << col_name << "]" << " successfully created." << endl;
	}
	else
	{
		cout << "[Error]: unknown column create error." << endl;
	}

	save();
}
bool Table::col_exists(const string& nm) const
{
	bool ret = false;
	for(vector<Column*>::const_iterator it = column.begin(); it != column.end() && !ret; ++it)
	{
		ret = (*it)->get_name() == nm;
	}
	return ret;
}
void Table::col_del()
{
	Pair p = column_selector();
	int sel = p.sel-1;
	int tsize = p.msize;

	if(sel < tsize+1)
	{
		delete column[sel];
		column.erase(column.begin() + sel);
	}

	save();
}
void Table::col_list() const
{
	cout << "### Column list ###" << endl;
	cout << "Name" << '\t' << "Type" << '\t' << "NULL" << endl;
	for(vector<Column*>::const_iterator it = column.begin(); it != column.end(); ++it)
	{
		cout << (*it)->get_name() << '\t' << (*it)->get_type_name() << '\t' << (*it)->get_is_null() << endl;;
	}
	cout << endl;
}
void Table::del(const bool& is_cond)
{
	cout << "DELETE from [" << name << "] table" << endl;
	if(is_cond)
	{
		Pair p = column_selector();
		int sel = p.sel-1;
		int tsize = p.msize;

		if(sel < tsize+1)
		{
			vector<int> clist = (column[sel])->select_list();
			for(vector<Column*>::const_iterator it = column.begin(); it != column.end(); ++it)
			{
				(*it)->del(clist);
			}
		}
	}
	else
	{
		for(vector<Column*>::const_iterator it = column.begin(); it != column.end(); ++it)
		{
			(*it)->del();
		}
	}

	save();
}
void Table::insert()
{
	cout << "INSERT INTO [" << name << "] table" << endl;
	for(vector<Column*>::const_iterator it = column.begin(); it != column.end(); ++it)
	{
		(*it)->insert();
	}

	save();
}
void Table::select(const bool& is_cond) const
{
	vector<stringstream> val;
	cout << "SELECT FROM [" << name << "] table" << endl;
	if(is_cond)
	{
		Pair p = column_selector();
		int sel = p.sel-1;
		int tsize = p.msize;

		if(sel < tsize+1)
		{
			vector<int> clist = (column[sel])->select_list();
			for(vector<Column*>::const_iterator it = column.begin(); it != column.end(); ++it)
			{
				val.push_back((*it)->select(clist));
			}
		}
		else
		{
			return;
		}
	}
	else
	{
		for(vector<Column*>::const_iterator it = column.begin(); it != column.end(); ++it)
		{
			val.push_back((*it)->select());
		}
	}

	for(vector<Column*>::const_iterator it = column.begin(); it != column.end(); ++it)
	{
		cout << ((*it)->get_name()) << '\t';
	}
	cout << endl;

	string temp;
	bool ok = true;
	while(ok)
	{
		for(int i = 0; i < static_cast<int>(val.size()) && ok; ++i)
		{
			val[i] >> temp;
			ok = !(val[i]).fail();
			if(ok)
			{
				cout << temp << '\t';
			}
		}
		cout << endl;
	}
}


void Table::save() const
{
	stringstream out;
	for(vector<Column*>::const_iterator it = column.begin(); it != column.end(); ++it)
	{
		if(it != column.begin())
		{
			out << ' ';
		}
		out << (*it)->get_name();
	}
	out << endl;
	for(vector<Column*>::const_iterator it = column.begin(); it != column.end(); ++it)
	{
		if(it != column.begin())
		{
			out << ' ';
		}
		out << (*it)->get_type_name();
	}
	out << endl;
	for(vector<Column*>::const_iterator it = column.begin(); it != column.end(); ++it)
	{
		if(it != column.begin())
		{
			out << ' ';
		}
		out << (*it)->get_is_null();
	}
	out << endl;
	for(vector<Column*>::const_iterator it = column.begin(); it != column.end(); ++it)
	{
		if(it != column.begin())
		{
			out << endl;
		}
		out << ((*it)->select()).str();
	}
	out << endl;

	string filename = db_name+"_"+name+".dat";
	ofstream f(filename);
	f << out.str();
	f.close();
}

void Table::drop()
{
	dropped = true;
}


string Table::get_name() const
{
	return name;
}

string& Table::dummy_data(string& s, const string& e, const int& n, const bool& null_allowed) const
{
	string elem = (null_allowed) ? "null" : e;
	for(int i = 0; i<n; ++i)
	{
		if(i != 0)
		{
			s += " ";
		}
		s += elem;
	}
	return s;
}

int Table::col_dat_qt() const
{
	int ret = 0;
	if(column.size() > 0)
	{
		ret = column[0]->get_size();
	}
	return ret;
}

Pair Table::column_selector() const
{
	int sel = 0;
	int tsize = static_cast<int>(column.size());

	cout << "Choose a column from the list" << endl;
	do
	{
		int i = 0;
		for(i=0; i<tsize; ++i)
		{
			cout << (i+1) << ". " << (column[i])->get_name() << endl;
		}
		cout << i+1 << ". back" << endl;
		sel = Validator::get<int>("Select an option: ");
	} while(sel <= 0 || sel > tsize+1);

	return Pair(sel, tsize);
}